package com.gitee.limaozhi.nio.example03;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * @author hzlimaozhi
 * @date 2019/12/20 15:04
 */
public class Client {

    public static void main(String[] args) throws IOException {
        // 1. 创建Channel
        SocketChannel socketChannel = SocketChannel.open();
        // 2. 设置非阻塞
        socketChannel.configureBlocking(false);
        // 设置发送缓冲区大小
        System.out.println("RCV_SIZE: " + socketChannel.socket().getSendBufferSize());
        System.out.println("SND_SIZE: " + socketChannel.socket().getReceiveBufferSize());
        socketChannel.socket().setSendBufferSize(1024);
        System.out.println("RCV_SIZE: " + socketChannel.socket().getSendBufferSize());
        System.out.println("SND_SIZE: " + socketChannel.socket().getReceiveBufferSize());
        socketChannel.socket().setTcpNoDelay(true);
        // 3. 发起连接
        socketChannel.connect(new InetSocketAddress(8000));

        // 发往服务器数据
        ByteBuffer writeBuffer = ByteBuffer.allocate(3 * 64 * 1024);
        {
            writeBuffer.clear();
            byte[] request = "This is request.".getBytes();
            while (writeBuffer.remaining() > 0) {
                writeBuffer.put(request, 0, Math.min(writeBuffer.remaining(), request.length));
            }
            writeBuffer.flip();
        }

        // 4. 创建Selector
        Selector selector = Selector.open();
        // 5. 向Selector注册事件
        socketChannel.register(selector, socketChannel.validOps(), writeBuffer);

        while (!socketChannel.socket().isClosed()) {
            // 6. 查询就绪通道和事件
            if (selector.select(10) <= 0) {
                continue;
            }

            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();

                if (selectionKey.isValid() && selectionKey.isConnectable()) {
                    System.out.println("\nConnectable");
                    handleConnectable(selectionKey);
                }

                if (selectionKey.isValid() && selectionKey.isWritable()) {
                    System.out.println("\nWritable");
                    handleWritable(selectionKey);
                }

                if (selectionKey.isValid() && selectionKey.isReadable()) {
                    System.out.println("\nReadable");
                    handleReadable(selectionKey);
                }
            }
        }

        selector.close();
    }

    private static void handleConnectable(SelectionKey selectionKey) throws IOException {
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
        boolean success = socketChannel.finishConnect();
        if (success) {
            System.out.println("建立连接");
        }
    }

    private static void handleWritable(SelectionKey selectionKey) throws IOException {
        ByteBuffer writeBuffer = (ByteBuffer) selectionKey.attachment();

        if (writeBuffer.remaining() > 0) {
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            int count = socketChannel.write(writeBuffer);
            System.out.println("写入：" + count);

            if (writeBuffer.remaining() == 0) {
                // 关闭写通道 => 通知对方结束读取
                System.out.println("关闭写通道");
                socketChannel.shutdownOutput();
            }
        }
    }

    private static void handleReadable(SelectionKey selectionKey) throws IOException {
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

        // 读缓冲
        ByteBuffer readBuffer = ByteBuffer.allocate(64);
        readBuffer.clear();

        int count = socketChannel.read(readBuffer);
        if (count > 0) {
            System.out.println("读取：" + count);

        } else if (count == 0) {
            System.out.println("无可读");

        } else {
            System.out.println("对方关闭写通道");

            System.out.println("关闭Socket");
            selectionKey.cancel();
            socketChannel.close();
        }
    }

}
